// UpdatePair.cpp

#include "StdAfx.h"

#include <time.h>

#include "Common/Defs.h"
#include "Common/FileSystem/Wildcard.h"

#include "Common/System/Time.h"

#include "Common/FileSystem/SortUtils.h"
#include "UpdatePair.h"

namespace Common
{
namespace Archive
{
using namespace System;
using namespace Time;


static int MyCompareTime(FileTimeType fileTimeType, const FILETIME &time1, const FILETIME &time2)
{
  switch(fileTimeType)
  {
    case FileTimeType::kWindows:
      return ::CompareFileTime(&time1, &time2);
    case FileTimeType::kUnix:
      {
        UINT32 unixTime1, unixTime2;
        FileTimeToUnixTime(time1, unixTime1);
        FileTimeToUnixTime(time2, unixTime2);
        return simpleCompare(unixTime1, unixTime2);
      }
    case FileTimeType::kDOS:
      {
        UINT32 dosTime1, dosTime2;
        FileTimeToDosTime(time1, dosTime1);
        FileTimeToDosTime(time2, dosTime2);
        return simpleCompare(dosTime1, dosTime2);
      }
  }
  throw 4191618;
}

static LPCTSTR kDuplicateFileNameMessage = _T("Duplicate filename:");
static LPCTSTR kNotCensoredCollisionMessaged = _T("Internal file name collision (file on disk, file in archive):");

static void ThrowError(const CString &message, const CString &s1, const CString &s2)
{
  CString m = message;
  m += _T('\n');
  m += s1;
  m += _T('\n');
  m += s2;
  throw m;
}

static void TestDuplicateString(const CStringVector &strings)
{
  for(size_t i = 0; i + 1 < strings.size() - 1; i++)
    if (Common::FileSystem::CompareFileNames(strings[i], strings[i + 1]) == 0)
      ThrowError(kDuplicateFileNameMessage, strings[i], strings[i + 1]);
}

void GetUpdatePairInfoList(
    const FileSystem::CDirItems &dirItems,
	const std::vector<FileSystem::CArcItem> &arcItems,
    FileTimeType fileTimeType,
    std::vector<CUpdatePair> &updatePairs)
{
  //CIntVector dirIndices, arcIndices;
  
  size_t numDirItems = dirItems.Items.size();
  size_t numArcItems = arcItems.size();
  
  
  {
    CStringVector arcNames;
    //arcNames.reserve(numArcItems);
    for (size_t i = 0; i < numArcItems; i++)
		arcNames.push_back(arcItems[i].Name);
    //FileSystem::SortFileNames(arcNames, arcIndices);
	std::sort(arcNames.begin(), arcNames.end());
    TestDuplicateString(arcNames);//, arcIndices);
  }

  CStringVector dirNames;
  {
    //dirNames.reserve(numDirItems);
    for (size_t i = 0; i < numDirItems; i++)
      dirNames.push_back(dirItems.GetLogPath(i));
	std::sort(dirNames.begin(), dirNames.end());
    //FileSystem::SortFileNames(dirNames, dirIndices);
    TestDuplicateString(dirNames);//, dirIndices);
  }
  
  size_t dirIndex = 0, arcIndex = 0;
  while (dirIndex < numDirItems && arcIndex < numArcItems)
  {
    CUpdatePair pair;
    int dirIndex2 = dirIndex;
    int arcIndex2 = arcIndex;
	const auto & di = dirItems.Items[dirIndex2];
    const Common::FileSystem::CArcItem &ai = arcItems[arcIndex2];
    int compareResult = Common::FileSystem::CompareFileNames(dirNames[dirIndex2], ai.Name);
    if (compareResult < 0)
    {
      pair.State = Update::PairState::State::kOnlyOnDisk;
      pair.DirIndex = dirIndex2;
      dirIndex++;
    }
    else if (compareResult > 0)
    {
      pair.State = ai.Censored ?
          Update::PairState::State::kOnlyInArchive:
          Update::PairState::State::kNotMasked;
      pair.ArcIndex = arcIndex2;
      arcIndex++;
    }
    else
    {
      if (!ai.Censored)
        ThrowError(kNotCensoredCollisionMessaged, dirNames[dirIndex2], ai.Name);
      pair.DirIndex = dirIndex2;
      pair.ArcIndex = arcIndex2;
      switch (ai.MTimeDefined ? MyCompareTime(
          ai.TimeType != - 1 ? (FileTimeType)ai.TimeType : fileTimeType,
		  di->ModificationTime, ai.MTime): 0)
      {
        case -1: pair.State = Update::PairState::State::kNewInArchive; break;
        case 1:  pair.State = Update::PairState::State::kOldInArchive; break;
        default:
          pair.State = (ai.SizeDefined && di->Size == ai.Size) ?
              Update::PairState::State::kSameFiles :
              Update::PairState::State::kUnknowNewerFiles;
      }
      dirIndex++;
      arcIndex++;
    }
	updatePairs.push_back(pair);
  }

  for (; dirIndex < numDirItems; dirIndex++)
  {
    CUpdatePair pair;
    pair.State = Update::PairState::State::kOnlyOnDisk;
    pair.DirIndex = dirIndex;
    updatePairs.push_back(pair);
  }
  
  for (; arcIndex < numArcItems; arcIndex++)
  {
    CUpdatePair pair;
    int arcIndex2 = arcIndex;
    pair.State = arcItems[arcIndex2].Censored ?
        Update::PairState::State::kOnlyInArchive:
        Update::PairState::State::kNotMasked;
    pair.ArcIndex = arcIndex2;
    updatePairs.push_back(pair);
  }

  updatePairs.shrink_to_fit();
}

}
}